#include "mime.h"
#include "contenttype.h"
#include "contentdisposition.h"

MIME*   
mime_new(char *bpath)
{
    MIME    *mime;

    mime = (MIME*)emalloc(sizeof(MIME));
    if (NULL == mime)
        return NULL;
    memset(mime, 0, sizeof(MIME));
    mime->basepath = bpath;

    return mime;
        
}


void    
mime_delete(MIME*  mime)
{
    if (NULL == mime)
        return ;
    
    mime_clear(mime);
    efree(mime);
}


void    
mime_clear(MIME*  mime)
{
    if (NULL == mime)
        return ;
    
    SAFE_FREE(mime->MimeVersion)
    SAFE_FREE(mime->ContentType)
    SAFE_FREE(mime->ContentTransferEncoding)
    SAFE_FREE(mime->ContentId)
    SAFE_FREE(mime->ContentDisposition)
    SAFE_FREE(mime->ContentMD5)
    SAFE_FREE(mime->ContentLanguage)
    SAFE_FREE(mime->Data)
    SAFE_FREE(mime->boundary)
}


int     
mime_set(void* mm, char* key, char* value,int vlen)
{
    MIME* mime = mm;
    if (NULL == mime)
        return -1;
    
    if (NULL == key)
        return -2;

    if (NULL == value)
        return -3;

    if (vlen <= 0)
        return -4;

    INFO("mime set key: %s, len: %d\n", key, vlen);
    if (strcmp(key, "mime-version") == 0)
    {
        mime->MimeVersion = xstrappend(mime->MimeVersion, value, (size_t)vlen);
    }else if (strcmp(key, "content-type") == 0){
        INFO("content-type! %c%c%c\n", value[0], value[1], value[2]);
        mime->ContentType = xstrappend(mime->ContentType, value, vlen);
        
        mime->ctype = check_maintype(mime->ContentType);

        if ('t' == mime->ctype) {
            if (strstr(mime->ContentType, "text/plain") != NULL)
            {
                mime->stype = 'p';
                INFO("text/plain\n");
            }else if (strstr(mime->ContentType, "text/html") != NULL){
                mime->stype = 'h';
                INFO("text/html\n");
            }
        }
                    
    }else if (strcmp(key, "content-transfer-encoding") == 0){
        mime->ContentTransferEncoding = xstrappend(mime->ContentTransferEncoding, value, vlen);
    }else if (strcmp(key, "content-id") == 0){
        mime->ContentId = xstrappend(mime->ContentId, value, vlen);
    }else if (strcmp(key, "content-disposition") == 0){
        mime->ContentDisposition = xstrappend(mime->ContentDisposition, value, vlen);
    }else if (strcmp(key, "content-md5") == 0){
        mime->ContentMD5 = xstrappend(mime->ContentMD5, value, vlen);
    }else if (strcmp(key, "content-language") == 0){
        mime->ContentLanguage = xstrappend(mime->ContentLanguage, value, vlen);
    }
    
    return 0;
}

static int 
mimedata_decode_line(MIME *mime, BUFFER* mailbuf, BUFFER **buf, char *data, int dlen, char tencode, int *writelen)
{
    int     ret;
    char    debuf[8192] = {0};
    
    if ('m' == mime->ctype)
        return 0;

    //INFO("before line: pos: %d, len:%d\n", mailbuf->pos, mailbuf->len);
    if (NULL == mime->Data) {
        if ('t' == mime->ctype) {
            if (mailbuf->filesize > 20480) { // 相信大多数的信件，text部分信息不会超过20k,除html以外
                INFO("malloc 20480 for mime->Dat\n");
                mime->Data = emalloc(20480);
                if (NULL == mime->Data) {
                    return -1;
                }
                mime->Data[0] = 0;
                mime->DataSize = 20480;
            }else{
                mime->Data = emalloc(mailbuf->filesize);
                if (NULL == mime->Data) {
                    return -2;
                }
                mime->Data[0] = 0;
                mime->DataSize = mailbuf->filesize;
            }
            //INFO("mime->Data malloc %d, %p\n", mime->DataSize, mime->Data);
        }else{ //不是text/类型的话，Data里仅仅是存储的一个文件路径，数据解码到这个文件中
            CONTENT_TYPE        *ctype;
            CONTENT_DISPOSITION *cdis;
            int     count = 0;
            
            mime->Data = emalloc(257); // 只存放路径，这个够了
            memset(mime->Data, 0, 257); 

            INFO("malloc 257 for mime->Data, %p\n", mime->Data);
            // 从content-disposition中取文件名
            if (NULL != mime->ContentDisposition) {
                char namebuf[1024];
                INFO("get filename from content-disposition...\n");
                cdis = contentdis_new();
                contentdis_parse(cdis, mime->ContentDisposition);
                
                if (NULL != cdis->FileName) {
                    INFO("orig: %s\n", cdis->FileName);
                    text_decode(namebuf, cdis->FileName, "GB2312");
                    INFO("namebuf: %s\n", namebuf);
                    snprintf(mime->Data, 256, "%s/%s", mime->basepath, namebuf);
                } 
                while (isfile(mime->Data)) {
                    snprintf(mime->Data, 256, "%s/%d.%s", mime->basepath, count, namebuf);
                    count++;
                }
                contentdis_delete(cdis);
                INFO("from content-disposition: %s\n", mime->Data);
            }

            if (0 == mime->Data[0]) { // 从content-type中取文件名
                if (NULL != mime->ContentType) {
                    char namebuf[1024];

                    INFO("get name from content-type...\n");
                    ctype = contenttype_new();
                    contenttype_parse(ctype, mime->ContentType);

                    if (NULL != ctype->Name) {
                        INFO("orig: %s\n", ctype->Name);
                        text_decode(namebuf, ctype->Name, "GB2312");
                        INFO("namebuf: %s\n", namebuf);
                        snprintf(mime->Data, 256, "%s/%s", mime->basepath, ctype->Name);
                    }
                    while (isfile(mime->Data)) {
                        snprintf(mime->Data, 256, "%s/%d.%s", mime->basepath, count, namebuf);
                        count++;
                    }

                    contenttype_delete(ctype);
                    INFO("from content-type: %s\n", mime->Data);
                }
            }

            if (0 == mime->Data[0]) { // 只好自己生成一个路径了
                char    *filename = "unkown";

                snprintf(mime->Data, 256, "%s/%s", mime->basepath, filename);
                while (isfile(mime->Data)) {
                    snprintf(mime->Data, 256, "%s/%d.%s", mime->basepath, count, filename);
                    count++;
                }
            }
            INFO("mime->Data: %s\n", mime->Data);
            fprintf(stderr, "\t%s\n", mime->Data);
            *buf = buf_new(4096, mime->Data, 1);
        }
        *writelen = 0;
    }
    if ('t' == mime->ctype) {
        if (*writelen + dlen >= mime->DataSize) {
            char *newdata;

            mime->DataSize += 20480;
            INFO("realloc mime->Date to size:%d\n", mime->DataSize);
            newdata = emalloc(mime->DataSize);
            if (NULL == newdata) {
                return -3;
            }
            memcpy(newdata, mime->Data, *writelen);
            SAFE_FREE(mime->Data);
            mime->Data = newdata;
            INFO("mime->Data: %p\n", mime->Data);
        }
    }
    
    switch(tencode)
    {
        case 0:
        case 7: // 7-bit
        case 8: // 8-bit
            if ('t' == mime->ctype) {
                strncpy(mime->Data+ *writelen, data, dlen);
                *writelen += dlen;
            }else{
                buf_writen(*buf, data, dlen);
            }
            break;
        case 'b':
            {
                int     yu;
                // dlen必须是4的倍数，不然下行解码肯定会有问题的
                
                // 开头两个字母是--, 可能是出错的boundary, 所以不解析
                if (dlen > 2 && data[0] == '-' && data[1] == '-') {
                    return 0;
                }
                yu = dlen % 4;
                //INFO("yu: %d,len:%d, %c%c%c, %x %x %x\n", yu, dlen,data[dlen-3], data[dlen-2], data[dlen-1], data[dlen-3], data[dlen-2], data[dlen-1]);
                /*{
                    int i;
                    printf("\n==============================\n");
                    for (i = 0; i < dlen; i++) {
                        printf("%c", data[i]);
                    }
                    printf("\n------------------------------\n");
                    for (i = 0; i < dlen; i++) {
                        printf("%x ", data[i]);
                    }
                    printf("\n");

                }*/
                // 注意：在这里必须判断buf->pos < buf->len, 否则当在文件的最后的时候，如果yu>0，则有可能会无限循环。
                while (yu > 0) { // 不是4的倍数的话，把这几个字符放到下一行来处理
                    if (feof(mailbuf->file)) {
                        if (mailbuf->pos == mailbuf->len)
                            break;
                    }

                    if (data[dlen] == '\r') {
                        memmove(&data[dlen], &data[dlen-yu], yu);
                        data[dlen-yu] = '\r';
                        data[dlen-yu + 1] = '\n';
                        mailbuf->pos -= yu;
                    }else if (data[dlen] == '\n') {
                        memmove(&data[dlen], &data[dlen-yu], yu);
                        data[dlen-yu] = '\n';
                        mailbuf->pos -= yu;
                    }else{
                        mailbuf->pos -= yu;
                    }
                    dlen -= yu;
                    break;
                }

                if ('t' == mime->ctype) {
                    ret = base64_decode(mime->Data+ *writelen, data, dlen, 0);
                    if (0 > ret) {
                        INFO("base64 decode error!\n");
                        return -4;
                    }

                    *writelen += ret;
                    mime->Data[*writelen] = 0;
                    //INFO("data: %s\n", mime->Data+*writelen-ret);
                } else {
                    ret = base64_decode(debuf, data, dlen, 0);
                    if (0 > ret) {
                        INFO("base64 decode error!\n");
                        return -4;
                    }

                    ret = buf_writen(*buf, debuf, ret);
                }
            }
            break;
        case 'q':
            if ('t' == mime->ctype) {
                ret = qp_decode(mime->Data+ *writelen, data, dlen);
                if (0 > ret) {
                    INFO("qp decode error!\n");
                    return -5;                    
                }
                *writelen += ret;
                /*
                mime->Data[*writelen] = '\r';
                *writelen += 1;
                mime->Data[*writelen] = '\n';
                *writelen += 1;
                */
                mime->Data[*writelen] = 0;
                //INFO("writelen: %d\n", *writelen);
            }else{
                ret = qp_decode(debuf, data, dlen);
                if (0 > ret) {
                    INFO("qp decode error!\n");
                    return -5;                    
                }

                //INFO("qp decode %d\n", ret);
                buf_writen(*buf, debuf, ret);
            }

            break;
        default:
            INFO("not support this transfer encoding .\n");
            break;
    }
    
    //INFO("after line: pos: %d, len:%d\n", mailbuf->pos, mailbuf->len);
    return 0;
}


int     
mime_parse(MIME* mime, BUFFER* buf, int is_parse_data, int have_head, GHashTable *boundary)
{
    int     ret;
    char    *start;
    int     startlen, dlen, len;
    //char    boundary[513] = {0};
    int     is_blank; // 是否为头信息或者数据信息开始的空行
    int     split_error = 0;
    CONTENT_TYPE    *ctype;
    char    tencode = 0; // 传输编码
    BUFFER  *mbuf = NULL;
    int     wlen;

    if (NULL == mime)
        return -1;

    if (NULL == buf)
        return -2;

    //mime_clear(mime);
    // boundary 已经过了
    INFO("boundary size: %d\n", g_hash_table_size(boundary)); 
    if (have_head) {
        INFO("perform mime head ...\n");
        while (1) {
            INFO("before read: pos: %d, len:%d\n", buf->pos, buf->len);
            ret = buf_getfield(buf, &start, &len);
            INFO("buf_getfield: %d\n", ret);
            if (ret <= 0) {
                INFO("parse at end of file.\n");
                break;
            }
            if ((ret == 1) || (ret == 2 && start[0] == '\r'))  { //长度只有1的话，一个是只有\n, 是2的话也有可能是\r\n
                mime->HeadLen = buf_file_pos(buf) - ret - mime->HeadStart;
                INFO("found mime head endpos\n");
                break;
            }
            ret = fields_split(start, len, mime, mime_set);
            if (ret == -3) {
                split_error++;
                INFO("not found : , error: %d\n", split_error);
            }
            if (split_error >= 3) {
                //连续3行都没有发现:, 在信件头中是不可能的, 必定是信件格式有问题
                mime->HeadLen = buf_file_pos(buf) - mime->HeadStart;
                INFO("error 3 , endpos: %d, len:%d\n", mime->HeadStart, mime->HeadLen);
                return 1;
            }
        }

        if (NULL != mime->ContentType) {
            ctype = contenttype_new();
            contenttype_parse(ctype, mime->ContentType);
            if (ctype->Boundary) {
                char *mbound = xstrdup(ctype->Boundary);
                xstrnlower(mbound, strlen(mbound));
                INFO("mbound: %s\n", mbound);
                g_hash_table_insert(boundary, mbound, mime);
            }
            contenttype_delete(ctype);
        }
        // 跳过数据部分开始的空行，不知道究竟有多少行
        buf_skips(buf, "\r\n", 2); 
        // 如果不是boundary, 应该下面的内容直接就是信件正文了
    } 

    if (mime->ContentTransferEncoding) {
        if (strcmp(mime->ContentTransferEncoding, "base64") == 0) {
            tencode = 'b';
        }else if (strcmp(mime->ContentTransferEncoding, "quoted-printable") == 0) {
            tencode = 'q';
        }
    }

    // 数据行了
    INFO("perform mime data ...%c%c\n", buf->data[buf->pos], buf->data[buf->pos+1]);
    mime->DataStart = buf_file_pos(buf); 
    while (1)
    {
        ret = buf_getline(buf, &start, &startlen);
        //INFO("ret = %d, %d %d %d\n", ret, buf->data[buf->pos], buf->data[buf->pos+1], buf->data[buf->pos+2]);
        if (ret == 0) {// mime的数据信息结束
            mime->DataLen = buf_file_pos(buf) - mime->DataStart - ret;
            INFO("getline return 0\n");
            return 0;
        }else if (ret == -100) {
            // 肯定是一行太长了
            INFO("not found \\n\n");
            len = buf_readn(buf, buf->max, &start);
            INFO("read %d\n", len);
            if (0 == len) {
                mime->DataLen = buf_file_pos(buf) - mime->DataStart;
                break;
            }
            startlen = len;
        }else if (*start == '-' && *(start+1) == '-') {
            char    *bound, *btmp;
            MIME    *m;
            int     boundlen;

            INFO("start --\n");
            bound = xstrsdup(start+2, "\r\n", 2);
            if (NULL == bound) { // 如果只是--\r\n，就可能是NULL
                continue;
            }
            INFO("bound: %s\n", bound);
            boundlen = strlen(bound);
            if (bound[boundlen-1] == '-') { // 说明这个boundary后面没有数据
                bound[boundlen-2] = 0;
                //bound[boundlen-1] = 0;
            }
            xstrnlower(bound, boundlen);
            INFO("try bound: %s, len: %d, hash: %u, hashtable size: %d\n", bound, strlen(bound), g_str_hash(bound), g_hash_table_size(boundary));
            m = g_hash_table_lookup(boundary, bound);
            if (NULL != m) {
                INFO("ok, found boundary...\n");
                if (NULL == mime->boundary) {
                    mime->boundary = bound;
                }else{
                    efree(bound);
                }
                mime->DataLen = buf_file_pos(buf) - mime->DataStart - startlen;
                buf_ungetline(buf, &start, &len); 
                INFO("unget: %c%c%c\n", buf->data[buf->pos], buf->data[buf->pos+1], buf->data[buf->pos+2]);
                break;
            }
            INFO("not match boundary...\n");
            efree(bound); 
        }
        // 开始处理数据了。。
        //INFO("parse data...\n");
        /*
        {
            int i;
            //INFO("startlen: %d\n", startlen);
            for (i = 0; i< startlen; i++)
            {
                printf("%c", start[i]);
                
            }
            printf("\n");
        }*/
        if (is_parse_data) {
            if ('b' == tencode || 'q' == tencode) {
                if (start[startlen-2] == '\r') {
                    startlen -= 2; 
                }else if (start[startlen-1] == '\n') {
                    startlen--;
                }
            }
            //INFO("decode line ...\n");            
            mimedata_decode_line (mime, buf, &mbuf, start, startlen, tencode, &wlen);
        }
          
    }
    if (NULL != mbuf) {
        buf_delete(mbuf);
        mbuf = NULL;
    }
    return 0; 
}


void
mime_print(MIME *mime)
{
    if (NULL == mime)
        return ;

    printf("MIME\n");
    printf("\tMimeVersion: %s\n", mime->MimeVersion);
    printf("\tContentType: %s\n", mime->ContentType);
    printf("\tContentTransferEncoding: %s\n", mime->ContentTransferEncoding);
    printf("\tContentId: %s\n", mime->ContentId);
    printf("\tContentDisposition: %s\n", mime->ContentDisposition);
    printf("\tContentMD5: %s\n", mime->ContentMD5);
    printf("\tContentLanguage: %s\n", mime->ContentLanguage);

    printf("\tHeadStart: %d, Len: %d\n", mime->HeadStart, mime->HeadLen);
    printf("\tDataStart: %d, Len: %d\n", mime->DataStart, mime->DataLen);
    printf("\tboundary: %s\n", mime->boundary);
    printf("\tstype: %d, %c\n", mime->stype, mime->stype);
    printf("\tctype: %d, %c\n", mime->ctype, mime->ctype);
    if (mime->DataSize < 512) {
        printf("\tData: %s\n", mime->Data);
    }
}

int 
mime_string (MIME *mime, char *save, int slen)
{
    if (NULL == mime)
        return -1;

    return 0;
}


#ifdef MAIN_TEST

int
main()
{
    MIME    *mime;
    char    *test = "Content-Type: multipart/related;\n\tboundary=\"=====002_Dragon330546054664_=====\";type=\"multipart/alternative\"\n";
    char    *test2 = "Content-Transfer-Encoding: base64";
    int     ret;

    mime = mime_new(); 

    if (NULL == mime)
        printf("NULL == mime\n");

    printf("%s\n", test);
    ret = mime_parse(mime, test, strlen(test));

    printf("ret = %d\n", ret);

    mime_print(mime);

    ret = mime_parse(mime, test2, strlen(test2));

    printf("ret = %d\n", ret);

    mime_print(mime);


    mime_delete(mime);
    return 0;
}




#endif



